/*
 * ruletangle.cpp
 *
 *  Created on: Jun 25, 2011
 *      Author: Mo
 */

#include "ruletangle.h"
#include <assert.h>

int Ruletangle::divideMajorsOn (const Coordinate& coo)
{
	Rectangle rT, rTR, rR, rBR, rB, rBL, rL, rTL;
	int subRectNum = 0;
	bool needToUpdate = false;
	vector<Rectangle>::iterator it = this->majorRectangle.begin();
	while (needToUpdate == false && it != this->majorRectangle.end())
		if (it->cooIsInside(coo))
		{
			needToUpdate = true;
			it->divide(coo, rT, rTR, rR, rBR, rB, rBL, rL, rTL); //The coo is NOT in the it->coos, it belongs to the minor list.
		}
		else
			it++;

	if (needToUpdate)
	{
		this->majorRectangle.erase(it);
		if (rT.gettotalCoo() > 0)
		{
			this->majorRectangle.push_back(rT);
			subRectNum++;
		}
		if (rTR.gettotalCoo() > 0)
		{
			this->majorRectangle.push_back(rTR);
			subRectNum++;
		}
		if (rR.gettotalCoo() > 0)
		{
			this->majorRectangle.push_back(rR);
			subRectNum++;
		}
		if (rBR.gettotalCoo() > 0)
		{
			this->majorRectangle.push_back(rBR);
			subRectNum++;
		}
		if (rB.gettotalCoo() > 0)
		{
			this->majorRectangle.push_back(rB);
			subRectNum++;
		}
		if (rBL.gettotalCoo() > 0)
		{
			this->majorRectangle.push_back(rBL);
			subRectNum++;
		}
		if (rL.gettotalCoo() > 0)
		{
			this->majorRectangle.push_back(rL);
			subRectNum++;
		}
		if (rTL.gettotalCoo() > 0)
		{
			this->majorRectangle.push_back(rTL);
			subRectNum++;
		}
	}

	return subRectNum;
}

void Ruletangle::addMinorCoo (const Coordinate& coo)
{
	this->divideMajorsOn (coo);

	//there's no need to merge if there's no minor rectangle yet.
	if (this->getMinorRectNum() == 0)
	{
		this->minorRectangle.push_back(Rectangle(coo));
		return;
	}

	int minDistIndex = this->minorRectangle[0].minDistance(coo);
	int minDistance = 0;
	for (int i = 0; i != this->getMinorRectNum(); i++)
		if (minDistIndex > this->minorRectangle[i].minDistance(coo))
		{
			minDistIndex = i;
			minDistance = this->minorRectangle[i].minDistance(coo);
		}


}

Ruletangle::Ruletangle (void)
{
	//this->majorLongInstance = new LongInstance();
	//this->minorLongInstance = new LongInstance();
}

Ruletangle::Ruletangle(const Ruletangle& other)
{
	this->majorLongInstance = other.majorLongInstance;
	this->minorLongInstance = other.minorLongInstance;
	for (int i = 0; i < (int) other.majorRectangle.size(); i++)
		this->majorRectangle.push_back(other.majorRectangle[i]);
	for (int i = 0; i < (int) other.minorRectangle.size(); i++)
		this->minorRectangle.push_back(other.minorRectangle[i]);
}


Ruletangle::Ruletangle(const LongInstance& l)
{
	cout << "Creating a Ruletangle object from a single no conflicting RuleBase object.\n";
	assert (l.getConflictNum() == 0 && "Error: Creating a ruletangle with only one [conflict-free] rule but the number of conflicting coordinates is not zero.");
	this->majorLongInstance = l;
	//this->minorLongInstance = l;
}

Ruletangle::Ruletangle(const LongInstance& l1, const LongInstance& l2)
{
	cout << "Creating a Ruletangle object from a pair of conflicting RuleBase objects.\n";
	assert (l1.getLabel() != l2.getLabel() && l1.hasSameFeature(l2) &&
		"rules l1 and l2 should have the same IF part and different THEN part");
	int majorSize, minorSize;
	bool l1IsDominant;
	if (l1.getOccuranceNum() >= l2.getOccuranceNum())
	{
		l1IsDominant = true;
		majorSize = l1.getOccuranceNum();
		minorSize = l2.getOccuranceNum();
		this->majorLongInstance = l1;
		this->minorLongInstance = l2;
	}
	else
	{
		l1IsDominant = false;
		majorSize = l2.getOccuranceNum();
		minorSize = l1.getOccuranceNum();
		this->majorLongInstance = l2;
		this->minorLongInstance = l1;
	}
	this->majorRectangle.clear();
	this->minorRectangle.clear();
	bool minorCooIsInMinorRect = false;
	bool minorCooCanBeInMinorRect = true;
	bool minorCooIsAdded = false;

	Coordinate currentMinCoo;
	Coordinate currentMajCoo;
	Rectangle currentMinRect;
	Rectangle tempRect;
	for (int i = 0; i < minorLongInstance.getOccuranceNum(); i++) //adding the minor coos one by one
	{
		currentMinCoo = this->minorLongInstance.getCoordinate(i);
		minorCooIsAdded = false;
		minorCooIsInMinorRect = false;
		minorCooCanBeInMinorRect = true;
		
		//first check to see if there is already a minor rectangle that covers current coo
		for (int j = 0; j < (int) this->minorRectangle.size() && !minorCooIsAdded; j++)
		{
			currentMinRect = this->minorRectangle[j];
			if (currentMinRect.cooIsInside(currentMinCoo))//if such minor rect exists, just add this coo
			{
				minorCooIsInMinorRect = true;
				this->minorRectangle[j].addCooNoCheck(currentMinCoo);
				minorCooIsAdded = true;
			}
		}

		//now if no minor rect covers this coo. Now see if any minor rect can be extended without crossing a major coo
		if (!minorCooIsAdded) 
			for (int j = 0; j < (int) this->minorRectangle.size() && !minorCooIsAdded; j++)
			{
				currentMinRect = this->minorRectangle[j];
				tempRect = currentMinRect.virtualExpand(currentMinCoo);
				minorCooCanBeInMinorRect = true;
				for (int k = 0; k < this->majorLongInstance.getOccuranceNum() && minorCooCanBeInMinorRect; k++)
				{
					currentMajCoo = this->majorLongInstance.getCoordinate(k);
					if (tempRect.cooIsInside(currentMajCoo))
						minorCooCanBeInMinorRect = false;
				}
				if (minorCooCanBeInMinorRect)
				{
					this->minorRectangle[j].addCooNoCheck(currentMinCoo);
					minorCooIsAdded = true;
				}
			}
		if (!minorCooIsAdded) 
			//Now that no minor rect includes this coo, and no minor rect can be expanded, we create a new one.
			this->minorRectangle.push_back(Rectangle(currentMinCoo, this->minorLongInstance));
	}
}

/** This is the old constructor, where we created a maximum major rectangle and added the minor 
 *  coordinates one by one. Adding each minor rectangle can break a major rectangle. In the end, 
 *  we will have a set of major rectangles and minor rectangles. However, we need only minor rectangles.
 */
//Ruletangle::Ruletangle(const LongInstance& l1, const LongInstance& l2)
//{
//	int majorSize, minorSize;
//	bool l1IsDominant;
//	if (l1.getOccuranceNum() >= l2.getOccuranceNum())
//	{
//		l1IsDominant = true;
//		majorSize = l1.getOccuranceNum();
//		minorSize = l2.getOccuranceNum();
//		this->majorLongInstance = l1;
//		this->minorLongInstance = l2;
//	}
//	else
//	{
//		l1IsDominant = false;
//		majorSize = l2.getOccuranceNum();
//		minorSize = l1.getOccuranceNum();
//		this->majorLongInstance = l2;
//		this->minorLongInstance = l1;
//	}
//	int t,b,l,r;
//	l1.getCooBorders(t, b, l, r);
//	Coordinate ul(t,l);
//	Coordinate lr(b,r);
//	this->majorRectangle.push_back(Rectangle(ul,lr,l1));
//	//vector<Coordinate>::iterator minorIt = this->minorLongInstance.coordinate.begin();
//	//for (; it != this->minorLongInstance.coordinate.end(); it++)
//	//  	this->addMinorCoo(it); //TBR: this adds a coordinate to the minor rectangles and divide the major rectangles as necessary.
//	for (int i = 0; i < this->minorLongInstance.getCooNum(); i++)
//		this->addMinorCoo(this->minorLongInstance.getCoordinate(i));
//
//}

Ruletangle::~Ruletangle()
{

}

bool Ruletangle::hasCoo (const Coordinate& coo)
{
	assert (this->majorLongInstance.getOccuranceNum() > 0);
	int i = 0;
	while (i != this->majorLongInstance.getCooNum()
			&& this->majorLongInstance.getCoordinate(i).isNotEqual(coo))
		i++;
	if (this->majorLongInstance.getCoordinate(i).isEqual(coo))
			return true;
	else if (this->minorLongInstance.getOccuranceNum() > 0)
	{
		i = 0;
		while (i != this->minorLongInstance.getCooNum()
				&& this->minorLongInstance.getCoordinate(i).isNotEqual(coo))
			i++;
		if (this->minorLongInstance.getCoordinate(i).isEqual(coo))
			return true;
	}
	return false;
}

bool Ruletangle::applies (const vector<Neighborhood*>& nbr) const
{
	assert( (int)nbr.size() == this->majorLongInstance.neighborhoodNum()
			&& "\nError: the given IF part has not the same lenghts as of the LongInstance's IF part.");

	if (this->majorLongInstance.hasSameFeature(nbr))
		return true;
	else
		return false;
}

int Ruletangle::label (const Coordinate& coo) const
{
	assert (this->majorLongInstance.getOccuranceNum() > 0);
	//assert (false && "Error in implementation. Check the exception rectangles, not the pixels.");

	//for (int i = 0; i < this->minorLongInstance.getCooNum(); i++)
	//	if (coo.isEqual(this->minorLongInstance.getCoordinate(i)))
	//		return this->minorLongInstance.getLabel();

	for (int i = 0; i < (int)this->minorRectangle.size(); i++)
		if (minorRectangle[i].cooIsInside(coo))
			return this->minorLongInstance.getLabel();

	return this->majorLongInstance.getLabel();
	//OLD IMPLEMENTATION. Why the hell did I write it like this?
	//int i = 0;
	//while (i != this->majorLongInstance.getCooNum()
	//		&& this->majorLongInstance.getCoordinate(i).isNotEqual(coo))
	//	i++;
	//if (this->majorLongInstance.getCoordinate(i).isEqual(coo))
	//		return this->majorLongInstance.getLabel();
	//else if (this->minorLongInstance.getOccuranceNum() > 0)
	//{
	//	i = 0;
	//	while (i != this->minorLongInstance.getCooNum()
	//			&& this->minorLongInstance.getCoordinate(i).isNotEqual(coo))
	//		i++;
	//	if (this->minorLongInstance.getCoordinate(i).isEqual(coo))
	//		return this->minorLongInstance.getLabel();
	//}
	//return -1;
}

void Ruletangle::print(void) const
{
	cout << endl << "This Ruletangle object has:" << endl;
	cout << "Major Coordinates: " << this->majorLongInstance.getCooNum() << endl;
	cout << "Major Rectangles: " << this->majorRectangle.size() << endl;
	cout << "Minor Coordinates: " << this->minorLongInstance.getCooNum() << endl;
	cout << "Minor Rectangles: " << this->minorRectangle.size() << endl;
}

void Ruletangle::printDetail(void) const
{
	cout << endl << "This Ruletangle object has:" << endl;
	cout << "Major Coordinates: " << this->majorLongInstance.getCooNum() << endl;
	cout << "Major Rectangles: " << this->majorRectangle.size() << endl;
	cout << "Minor Coordinates: " << this->minorLongInstance.getCooNum() << endl;
	cout << "Minor Rectangles: " << this->minorRectangle.size() << endl;
	
	for (int i = 0; i < (int) this->majorLongInstance.getCooNum(); i++)
	{
		cout << i << "-";
		this->majorLongInstance.getCoordinate(i).print();
		cout << " ";
	}
	for (int i = 0; i < (int) this->majorRectangle.size(); i++)
	{
		cout << i << "-";
		this->majorRectangle[i].print();
		cout << " ";
	}
	for (int i = 0; i < (int) this->majorLongInstance.getCooNum(); i++)
	{
		cout << i << "-";
		this->majorLongInstance.getCoordinate(i).print();
		cout << " ";
	}
	for (int i = 0; i < (int) this->minorRectangle.size(); i++)
	{
		cout << i << "-";
		this->minorRectangle[i].print();
		cout << " ";
	}
}

int Ruletangle::getNetInfoSizeInBits(void)
{
	/*
	  The effective information in an object:
	  one if-then rule and n rectangles (minor rectangles).
	  the rule size is "majorLongInstance.getFeatureSize()" number of bits for the if part and one bit for the "then" part.
	  the rectangles need n * 4 (4 coordinate is stored per rectangle) * BITS_PER_ROWCOL (number of bits per coordinate) bits.
	  */
	int majorLISize = (this->majorLongInstance.getFeatureSize() + 1) * BITS_PER_NBR;
	int minorLISize = (this->minorLongInstance.getFeatureSize() + 1) * BITS_PER_NBR;
	int majorRectSize = this->majorRectangle.size()*4*BITS_PER_ROWCOL;
	int minorRectSize = this->minorRectangle.size()*4*BITS_PER_ROWCOL;
	return (majorLISize + minorLISize + majorRectSize + minorRectSize);
}